#include<string>
#include<iostream>
#include<array>

class ConcreteComponentA;
class ConcreteComponentB;

class ConcreteVisitor1{
public:
    void VisitConcreteComponentA(const ConcreteComponentA *element) const;
    void VisitConcreteComponentB(const ConcreteComponentB *element) const;
};
class ConcreteVisitor2{
public:
    void VisitConcreteComponentA(const ConcreteComponentA *element) const;
    void VisitConcreteComponentB(const ConcreteComponentB *element) const;
};

class ConcreteComponentA {
 public:
  void Accept(ConcreteVisitor1 *visitor) const {
    visitor->VisitConcreteComponentA(this);
  }
  std::string ExclusiveMethodOfConcreteComponentA() const {
    return "A";
  }
};

class ConcreteComponentB {
 public:
  void Accept(ConcreteVisitor2 *visitor) const {
    visitor->VisitConcreteComponentB(this);
  }
  std::string SpecialMethodOfConcreteComponentB() const {
    return "B";
  }
};

void ConcreteVisitor1::VisitConcreteComponentA(const ConcreteComponentA *element) const {
    std::cout << element->ExclusiveMethodOfConcreteComponentA() << " + ConcreteVisitor1\n";
}

void ConcreteVisitor1::VisitConcreteComponentB(const ConcreteComponentB *element) const {
    std::cout << element->SpecialMethodOfConcreteComponentB() << " + ConcreteVisitor1\n";
}

void ConcreteVisitor2::VisitConcreteComponentA(const ConcreteComponentA *element) const {
    std::cout << element->ExclusiveMethodOfConcreteComponentA() << " + ConcreteVisitor2\n";
}
void ConcreteVisitor2::VisitConcreteComponentB(const ConcreteComponentB *element) const {
    std::cout << element->SpecialMethodOfConcreteComponentB() << " + ConcreteVisitor2\n";
}

void ClientCode1(ConcreteComponentA *compA, ConcreteVisitor1* visitor1) {
    compA->Accept(visitor1);
}

void ClientCode2(ConcreteComponentB *compB, ConcreteVisitor2* visitor2){
    compB->Accept(visitor2);
}

int main() {
  std::cout << "ConcreteComponentA Accepts ConcreteVisitor1:\n";
  ConcreteVisitor1 *visitor1 = new ConcreteVisitor1;
  ConcreteComponentA *compA;
  ClientCode1(compA, visitor1);
  std::cout << "\n";
  std::cout << "ConcreteComponentB Accepts ConcreteVisitor2:\n";
  ConcreteVisitor2 *visitor2 = new ConcreteVisitor2;
  ConcreteComponentB *compB;
  ClientCode2(compB, visitor2);

  delete compA;
  delete compB;
  delete visitor1;
  delete visitor2;

  return 0;
}